package net.butfly.bus.server.config.impl;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.butfly.bus.TX;
import net.butfly.bus.config.bean.ConnectionBean;
import net.butfly.bus.config.bean.NodeBean;
import net.butfly.bus.config.bean.NodeInvokerBean;
import net.butfly.bus.config.bean.RouterBean;
import net.butfly.bus.config.bean.TimeoutBean;
import net.butfly.bus.config.bean.TxBean;
import net.butfly.bus.context.Constants;
import net.butfly.bus.exception.BusRuntimeException;
import net.butfly.bus.server.ServerConfigInterface;
import net.butfly.bus.server.invoker.InvokerFactory;
import net.butfly.bus.server.invoker.InvokerProvider;
import net.butfly.bus.server.invoker.InvokerTypeFactory;
import net.butfly.bus.util.LogUtils;

import org.dom4j.Attribute;
import org.dom4j.Element;

public class ConfigXmlImp implements ServerConfigInterface {
	private static LogUtils logger = LogUtils.getLog();

	private Element configElement;

	private Map<String, NodeBean> nodeMap;

	private Map<String, TxBean> txMap;

	private Map<String, TimeoutBean> timeoutMap;

	private RouterBean router;

	@SuppressWarnings("unchecked")
	public ConfigXmlImp(Element configElement) {
		this.configElement = configElement;
		// -------------------------------------------------------------------------------解析node
		List<Element> nodeList = this.configElement.selectNodes("node");
		if (nodeList != null) {
			nodeMap = new HashMap<String, NodeBean>();
			for (Element nodeEle : nodeList) {
				String nodeId = nodeEle.attributeValue("id");
				String enabled = nodeEle.attributeValue("enabled");
				if (enabled == null || enabled.trim().toUpperCase().equals("TRUE")) {
					logger.info("加载节点 Id:" + nodeId + "\n	" + nodeEle.asXML());

					ConnectionBean connectionBean = null;
					/*
					 * now does not support remote connection to another server.
					 */

					// String connectionType = null;
					// String connectionId =
					// nodeEle.attributeValue("connection");// 连接类型
					// Element connectionEle;
					// if (connectionId != null) {
					// connectionEle = (Element)
					// this.configElement.selectSingleNode("connection[@id='" +
					// connectionId
					// + "']");
					// if (connectionEle == null) {
					// throw new
					// BusRuntimeException(Constants.UserError.NODE_CONFIG,
					// "没有找到 id为\"" + nodeId
					// + "\"的 node 所配置的 id为 \"" + connectionId +
					// "\" 的 connection");
					// }
					//
					// } else {
					// connectionEle = (Element)
					// nodeEle.selectSingleNode("connection");
					// }
					// if (connectionEle != null) {
					// connectionType = connectionEle.attributeValue("type");
					// if (connectionType == null) {
					// throw new
					// BusRuntimeException(Constants.UserError.NODE_CONFIG,
					// "id为 \"" + connectionId
					// + "\" 的 connection 没有配置 type 属性");
					// }
					// logger.info("节点(Id:" + nodeId + ")对应Connection:\n	" +
					// connectionEle.asXML());
					// Object connectionConfigObj =
					// ConnectionTypeFactory.getInstance().getPrototype(connectionType);
					// if (connectionConfigObj == null) {
					// throw new
					// BusRuntimeException(Constants.UserError.NODE_CONFIG,
					// "can not found connection type with name \"" +
					// connectionType
					// + "\" on nodes loading");
					// }
					// if (connectionConfigObj != null) {
					// try {
					// processConfigObj(connectionConfigObj, connectionEle);
					// } catch (Exception ex) {
					// throw new
					// BusRuntimeException(Constants.UserError.NODE_CONFIG,
					// "设置id为 " + connectionId
					// + " 的 Connection 参数出错", ex);
					// }
					// }
					// connectionBean = new ConnectionBean(connectionId,
					// connectionType, connectionConfigObj);
					// }

					NodeInvokerBean invokerBean = null;

					String invokerId = nodeEle.attributeValue("invoker");// 调用类型
					Element invokerEle;
					if (invokerId != null) {
						invokerEle = (Element) this.configElement.selectSingleNode("invoker[@id='" + invokerId + "']");
						if (invokerEle == null) { throw new BusRuntimeException(Constants.UserError.NODE_CONFIG,
								"没有找到 id为 \"" + nodeId + "\" 的 node 所配置的 id为 \"" + invokerId + "\" invoker"); }
					} else {
						invokerEle = (Element) nodeEle.selectSingleNode("invoker");
					}
					if (invokerEle != null) {
						String invokerType = invokerEle.attributeValue("type");
						if (invokerType == null) { throw new BusRuntimeException(Constants.UserError.NODE_CONFIG,
								"id为 \"" + invokerId + "\" 的 invoker 没有配置 type 属性"); }
						logger.info("节点(Id:" + nodeId + ")对应Invoker:\n	" + invokerEle.asXML());
						Object invokerConfigObj = InvokerTypeFactory.getInstance().getPrototype(invokerType);
						if (invokerConfigObj == null) { throw new BusRuntimeException(Constants.UserError.NODE_CONFIG,
								"can not found invoker type with name \"" + invokerType + "\" on nodes loading"); }
						try {
							processConfigObj(invokerConfigObj, invokerEle);
						} catch (Exception ex) {
							throw new BusRuntimeException(Constants.UserError.NODE_CONFIG, "设置id为 " + invokerId
									+ " 的 invoker 参数出错", ex);
						}
						invokerBean = new NodeInvokerBean(invokerId, invokerType, invokerConfigObj);
					}

					NodeBean nBean = new NodeBean(nodeId, nodeEle.attributeValue("txMatching"), connectionBean,
							invokerBean, null);
					InvokerProvider iip = InvokerFactory.getInstance().getProvider(nBean);
					if (iip != null) {
						logger.info("节点(Id:" + nodeId + ")对应Invoker(类型:" + invokerBean.getType() + ")初始化...");
						iip.init();
					} else {
						throw new BusRuntimeException(Constants.UserError.NODE_CONFIG, "节点(Id:" + nodeId
								+ ")配置的Invoker(类型:" + invokerBean.getType() + ")配置错误，系统没有找到相应的InvokerProvider实例.");
					}
					nodeMap.put(nodeId, nBean);
					logger.info("节点(Id:" + nodeId + ")加载完成.");
				} else {
					logger.info("节点(Id:" + nodeId + ")未启用:\n	" + nodeEle.asXML());
				}
			}
		}
		// -------------------------------------------------------------------------------解析交易
		List<Element> eleList = this.configElement.selectNodes("tx");
		if (eleList != null) {
			txMap = new HashMap<String, TxBean>();
			String[] nodeIdAry;
			TxBean txBean;
			for (Element ele : eleList) {
				String ts = ele.attributeValue("nodes");
				if (ts != null) {
					nodeIdAry = ts.split(",");
				} else {
					nodeIdAry = null;
				}
				txBean = new TxBean(ele.attributeValue("code"), ele.attributeValue("version"), nodeIdAry);
				logger.info("加载交易(交易代码 " + txBean.getCode() + "(" + txBean.getVersion() + ")" + ",节点位置 " + nodeIdAry
						+ ").");
				txMap.put(txBean.getKey(), txBean);
			}
		}
		// -------------------------------------------------------------------------------解析超时器
		List<Element> timeoutEleList = this.configElement.selectNodes("timeout");
		if (timeoutEleList != null) {
			timeoutMap = new HashMap<String, TimeoutBean>();
			TimeoutBean tb;
			String txMatching;
			String[] txMAry;
			for (Element ele : timeoutEleList) {
				txMatching = ele.attributeValue("txCode");
				tb = new TimeoutBean(ele.attributeValue("value") != null ? Long.valueOf(ele.attributeValue("value"))
						: 0, txMatching);
				txMAry = txMatching.split(",");
				for (String ts : txMAry) {
					timeoutMap.put(ts.trim().toUpperCase(), tb);
				}
			}
		}
		// -------------------------------------------------------------------------------解析router
		Element routerEle = (Element) this.configElement.selectSingleNode("router");
		if (routerEle != null) {
			router = new RouterBean(routerEle.attributeValue("type"), routerEle.attributeValue("directory"),
					routerEle.attributeValue("policy"));
		} else {
			router = new RouterBean("default", "simple", "random");// 默认路由器
		}
		logger.info("配置文件解析完成.");
	}

	public NodeBean getNodeBean(String nodeID) {
		return nodeMap != null ? nodeMap.get(nodeID) : null;
	}

	public RouterBean getRouterBean() {
		return router;
	}

	public String[] getNodeIDsByMatching(String txCode) {
		if (txMap != null) {
			for (Entry<String, TxBean> et : txMap.entrySet()) {
				if (et.getValue().matching(txCode)) { return et.getValue().getNodeAry(); }
			}
		}
		return null;
	}

	public TxBean getTxBean(String txCode, String versionNo) {
		return txMap != null ? txMap.get(getServiceKey(txCode, versionNo)) : null;
	}

	public TimeoutBean getTimeoutBean(String code) {
		if (timeoutMap != null) {
			TimeoutBean tb = timeoutMap.get(code.toUpperCase());
			if (tb != null) { return tb; }
			String ts;
			for (String matching : timeoutMap.keySet()) {
				if (matching.endsWith("*")) {
					ts = matching.substring(0, matching.indexOf("*"));
					if (code.startsWith(ts)) { return timeoutMap.get(matching); }
				}
			}
		}
		return null;
	}

	// --------------------------------------------------------------------------------
	public static String getServiceKey(TX tx) {
		return getServiceKey(tx.value(), tx.version());
	}

	public static String getServiceKey(String txCode, String versionNo) {
		return txCode + "-" + versionNo;
	}

	// ----------------------------------------------------------------------------------

	@SuppressWarnings("unchecked")
	private static void processConfigObj(Object configObj, Element element) throws IllegalArgumentException,
			IllegalAccessException {
		Class<?> clazz = configObj.getClass();
		while (!clazz.equals(Object.class)) {
			Field[] fAry = clazz.getDeclaredFields();
			if (fAry != null) {
				for (Field f : fAry) {
					Object v = null;
					if (List.class.isAssignableFrom(f.getType())) {// list类型
						List<Element> eleList = element.selectNodes(f.getName());
						if (eleList != null) {
							List<Object> itemList = new ArrayList<Object>();
							Object to;
							for (Element ele : eleList) {
								to = processConfigTag(ele);
								if (to != null) {
									itemList.add(to);
								}
							}
							v = itemList;
						}
					} else {
						Element ele = (Element) element.selectSingleNode(f.getName());
						if (ele != null) {
							Object to = processConfigTag(ele);
							if (to != null) {
								v = to;
							}
						}
					}
					if (v != null) {
						boolean af = f.isAccessible();
						f.setAccessible(true);
						f.set(configObj, v);
						f.setAccessible(af);
					}
				}
			}
			clazz = clazz.getSuperclass();
		}
	}

	private static Object processConfigTag(Element element) {
		if (element.attributeCount() > 0) {
			Map<String, String> rtn = new HashMap<String, String>();
			Iterator<?> it = element.attributeIterator();
			Attribute at;
			while (it.hasNext()) {
				at = (Attribute) it.next();
				rtn.put(at.getName(), at.getValue());
			}
			return rtn;
		}
		String tt = element.getTextTrim();
		if (tt != null && !tt.equals("")) { return tt; }
		return null;
	}
}
